#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <openssl/md5.h>
#include <glib.h>
#include <dlfcn.h>
#include <time.h>

#include "dict.h"
#include "date.h"
#include "encrypt.h"
#include "kylin-ukey.h"
#include "kylin-vikey.h"
#include "kylin-activation.h"
#include "kylin-verify.h"
#include "misc.h"
#include "kylin-license.h"

#include "type.h"

#include "ukey-macro.h"

#ifdef ENABLE_VIKEY

#include <vikey.h>


static int USER_SPACE = 1024;
static int USER_SPACE_OFFSET = 0;
//static int ADMIN_SPACE = 1024;
static int ADMIN_SPACE_OFFSET = 1024;

#define CODE_LENGTH 8
#define BUF_SIZE 4096
#define LINE_SIZE 256
#define MAX_LENGTH 4096
#define KEY_FILE_SIZE 512

#define VIKEY_SHARED_LIBRARY_PATH "/usr/lib/vikey/libvikey.so"

extern char* _kylin_activation_get_register_number(const char* serial_no, int* result);

#if defined __x86_64 || defined __x86_64__ || defined __amd64 || defined __amd64__
#define VIKEY_SHARED_LIBRARY_MD5SUM    "f9b91dc09adc2912661c720ddb58c1b2"
#define VIKEY_SHARED_LIBRARY_SHA1SUM   "cc31d982ed07207ddac3e34fdb78454e5314cb70"
#define VIKEY_SHARED_LIBRARY_SHA256SUM "7473b39d8f46a8a621974d2cd33aa3d5e6172a3e8a8f15992f122b820705bdc2"
#endif

#if defined __i386 || defined __i386__ || defined __i686 || defined __i686__
#define VIKEY_SHARED_LIBRARY_MD5SUM    "e4e1cb47d8452dc9e8a1a73f257eee91"
#define VIKEY_SHARED_LIBRARY_SHA1SUM   "4d618699f7b24484c2c15bba0c50c269cf404aa4"
#define VIKEY_SHARED_LIBRARY_SHA256SUM "5028ad840ce5ae9bffafdb511e9a5c44d591f14e065b9b25d30faf44b7f4ba4a"
#endif

#ifdef __aarch64__
#define VIKEY_SHARED_LIBRARY_MD5SUM    "57b3af6c18302c747288b81d63839c43"
#define VIKEY_SHARED_LIBRARY_SHA1SUM   "129c4dfc5ce17e82d95afa6ae063e4f43112740a"
#define VIKEY_SHARED_LIBRARY_SHA256SUM "ab194da807d61407f375b40704bc8d005c6c2cafb53c1c4958609e64c896f702"
#endif

//global variables in this file
static WORD vikey_index = 0;
static unsigned char vikey_data[BUF_SIZE];
static char dump_message[BUF_SIZE];
static GKeyFile* vikey_license_keyfile = NULL;
static int has_already_get = 0;

/*
 * vikey_get_hid_buf, get hid of vikey
 * return NO_ERROR if succeed, or error number if not
 */
static int vikey_get_hid_buf(char* buf, int size);

/*
 * vikey_init, init vikey environment
 * return NO_ERROR if succeed, or error number if not
 */
static int vikey_init();

/*
 * vikey_check_library, check if vikey library is valid
 * return NO_ERROR if succeed, or error number if not
 */
static int vikey_check_library();

DWORD (*my_VikeyFind)(DWORD* pdwCount) = NULL;
DWORD (*my_VikeyUserLogin)(WORD Index, char * pUserPassWord);
DWORD (*my_VikeyGetHID)(WORD Index, DWORD *pdwHID);
DWORD (*my_VikeyReadData)(WORD Index, WORD Addr, WORD Length, BYTE * buffer);
DWORD (*my_ViKeyDecraseModule)(WORD Index, WORD wModuleIndex);
DWORD (*my_ViKeyGetModule)(WORD Index, WORD wModuleIndex, WORD* pwValue);
DWORD (*my_ViKeyCheckModule)(WORD Index, WORD wModuleIndex, WORD *IsZero, WORD* CanDecrase);
DWORD (*my_VikeyWriteData)(WORD Index, WORD Addr, WORD Length, BYTE * buffer);
DWORD (*my_VikeyLogoff)(WORD Index);

DWORD (*my_VikeyGetType)(WORD Index, VikeyType * pType);

/*
 * vikey_find, find vikey
 * return NO_ERROR if vikey is inserted, or error number if not
 */
int vikey_find(DWORD* count)
{
	DWORD ret;

	ret = my_VikeyFind(count);
	if(ret == VIKEY_SUCCESS)
		return NO_ERROR;

	return NO_UKEY_FOUND;
}

/*
 * vikey_login, login vikey
 * return NO_ERROR if succeed, or error number if not
 */
static int vikey_login()
{
	char code[CODE_LENGTH + 1];
	char hid[LINE_SIZE];
	DWORD ret;

	code[CODE_LENGTH] = '\0';

	memset(hid, 0, sizeof(hid));

	ret = vikey_get_hid_buf(hid, sizeof(hid));
	if(ret != NO_ERROR)
		return ret;


	_hid_to_code(hid, code);

	ret = my_VikeyUserLogin(vikey_index, code);
	if(ret != VIKEY_SUCCESS)
		return UKEY_LOGIN_ERROR;

	return NO_ERROR;
}

/*
 * vikey_get_hid, get vikey hid
 * return vikey hid string on success, or NULL if failed
 */
char* vikey_get_hid()
{
	DWORD ret;
	DWORD hid;
	DWORD count;

	char buf[BUF_SIZE];

	ret = vikey_find(&count);
	if(ret != NO_ERROR)
		return NULL;

	ret = my_VikeyGetHID(vikey_index, &hid);
	if(ret != VIKEY_SUCCESS)
		return NULL;

	memset(buf, 0, BUF_SIZE);

	sprintf(buf, "%u", hid);

	return strdup(buf);
}

/*
 * vikey_get_hid_buf, get hid of vikey
 * return NO_ERROR if succeed, or error number if not
 */
static int vikey_get_hid_buf(char* buf, int size)
{
	DWORD ret;
	DWORD hid;
	DWORD count;

	ret = vikey_find(&count);
	if(ret != NO_ERROR)
		return ret;

	ret = my_VikeyGetHID(vikey_index, &hid);
	if(ret != VIKEY_SUCCESS)
		return UKEY_GET_HID_ERROR;

	memset(buf, 0, size);

	sprintf(buf, "%u", hid);

	return NO_ERROR;
}

/*
 * vikey_get_data, get data in vikey
 * return NO_ERROR if succeed, or error number if not
 */
static int vikey_get_data(int force)
{
	DWORD ret;

	if(!force && has_already_get)
		return NO_ERROR;

	ret = vikey_init();
	if(ret != NO_ERROR)
		return ret;

	ret = my_VikeyReadData(vikey_index, USER_SPACE_OFFSET, USER_SPACE + KEY_FILE_SIZE, vikey_data);
	if(ret != VIKEY_SUCCESS)
	{
		has_already_get = 0;
		return UKEY_GET_DATA_ERROR;
	}

	has_already_get = 1;

	return NO_ERROR;
}

/*
 * vikey_get_data_force, force to get data in vikey
 * return NO_ERROR if succeed, or error number if not
 */
static int vikey_get_data_force()
{
	DWORD ret;
	DWORD count;
	
	ret = vikey_find(&count);
	if(ret != NO_ERROR)
		return ret;

	ret = vikey_login();
	if(ret != NO_ERROR)
		return ret;

	return vikey_get_data(1);
}

/*
 * vikey_get_module, get count number from count module in vikey
 * return NO_ERROR if succeed, or error number if not
 */
static int vikey_get_module(unsigned short* count)
{
	DWORD ret;
	WORD module_index = 0;

	ret = vikey_init();
	if(ret != NO_ERROR)
		return ret;

	ret = my_ViKeyGetModule(vikey_index, module_index, count);
	if(ret != VIKEY_SUCCESS)
		return UKEY_MODULE_COUNT_ERROR;

	return NO_ERROR;
}

/*
 * vikey_check_module, check status of count module in vikey
 * is_zero: check if count number is zero
 * can_reduce: check if count number can be reduced
 * return NO_ERROR if succeed, or error number if not
 */
static int vikey_check_module(unsigned short* is_zero, unsigned short* can_reduce)
{
	DWORD ret;
	WORD module_index = 0;

	ret = vikey_init();
	if(ret != NO_ERROR)
		return ret;

	ret = my_ViKeyCheckModule(vikey_index, module_index, is_zero, can_reduce);
	if(ret != VIKEY_SUCCESS)
		return UKEY_MODULE_CHECK_ERROR;

	return NO_ERROR;
}

/*
 * vikey_decrease_module, decrease count number in count module
 * return NO_ERROR if succeed, or error number if not
 */
static int vikey_decrease_module()
{
	DWORD ret;
	WORD module_index = 0;
	unsigned short is_zero = 1;
	unsigned short can_reduce = 0;
	unsigned short before_decrease = 0;
	unsigned short after_decrease = 0;

	ret = vikey_init();
	if(ret != NO_ERROR)
		return ret;

	ret = my_ViKeyCheckModule(vikey_index, module_index, &is_zero, &can_reduce);
	if(ret != VIKEY_SUCCESS)
		return UKEY_MODULE_CHECK_ERROR;
	if(is_zero)
		return UKEY_MODULE_IS_ZERO;
	if(!can_reduce)
		return UKEY_MODULE_CANNOT_REDUCE;

	ret = my_ViKeyGetModule(vikey_index, module_index, &before_decrease);
	if(ret != VIKEY_SUCCESS)
		return UKEY_MODULE_COUNT_ERROR;

	if(0 == before_decrease)
		return UKEY_MODULE_NO_MORE_COUNT;

	ret = my_ViKeyDecraseModule(vikey_index, module_index);
	if(ret != VIKEY_SUCCESS)
		return UKEY_MODULE_DECREASE_ERROR;

	ret = my_ViKeyGetModule(vikey_index, module_index, &after_decrease);
	if(ret != VIKEY_SUCCESS)
		return UKEY_MODULE_COUNT_ERROR;

	if(before_decrease != (after_decrease + 1))
		return UKEY_MODULE_DECREASE_ERROR;

	return NO_ERROR;
}

/*
 * vikey_write_data, write data to vikey
 * return NO_ERROR if succeed, or error number if not
 */
static int vikey_write_data(unsigned char* buf, size_t offset, size_t length)
{
	DWORD ret;

	ret = vikey_init();
	if(ret != NO_ERROR)
		return ret;

	ret = my_VikeyWriteData(vikey_index, offset, length, buf);
	if(VIKEY_SUCCESS != ret)
		return UKEY_WRITE_DATA_ERROR;

	return NO_ERROR;
}

/*
 * vikey_add_register_code, add a register code into vikey
 * return NO_ERROR if succeed, or error number if not
 */
static int vikey_add_register_code(const char* register_code, int* _offset)
{
	DWORD ret;
	unsigned short module_count = 0;
	unsigned short aligned_space = 0;
	int offset = -1;
	unsigned short is_zero = 0;
	unsigned short can_reduce = 0;
	char buf[BUF_SIZE];

	if(!register_code_is_valid(register_code))
		return INVALID_REGISTER_CODE;

	ret = vikey_init();
	if(ret != NO_ERROR)
		return ret;

	ret = vikey_get_data_force();
	if(ret != NO_ERROR)
		return ret;

	ret = kylin_get_license(&vikey_license_keyfile, (const char*)&vikey_data[ADMIN_SPACE_OFFSET]);
	if(ret != NO_ERROR)
		return ret;

	ret = vikey_get_module(&module_count);
	if(ret == NO_ERROR)
	{
		if(0 == module_count)
			return UKEY_MODULE_NO_MORE_COUNT;
	} else {
		return ret;
    }

	memset(buf, 0, sizeof(buf));
	memcpy(buf, vikey_data, USER_SPACE);

//	ret = vikey_find_register_code(register_code, buf, sizeof(buf));
//	if(ret)
//		return REGISTER_CODE_ALREADY_IN_UKEY_SPACE;

	aligned_space = USER_SPACE - (USER_SPACE % SPACE_ALIGNMENT);
	offset = kylin_check_register_code_space_validation(buf, USER_SPACE);

	if(offset >= aligned_space)
		return UKEY_SPACE_FULL;

	ret = vikey_check_module(&is_zero, & can_reduce);
	if(ret != NO_ERROR)
		return ret;

	if(is_zero)
		return UKEY_MODULE_IS_ZERO;
	if(!can_reduce)
		return UKEY_MODULE_CANNOT_REDUCE;

	ret = vikey_write_data((unsigned char*)register_code, offset, ENCRYPTED_NUMBER_LENGTH);
	if(ret != NO_ERROR)
		return ret;

	if(_offset)
		*_offset = offset;

	return NO_ERROR;
}

/*
 * vikey_space_is_full, check if vikey space is full
 * return 1 if full, or 0 if not
 */
static int vikey_space_is_full(const char* buf, const size_t size)
{
	int aligned_space;
	int offset = 0;

	aligned_space = USER_SPACE - (USER_SPACE % SPACE_ALIGNMENT);
	offset = kylin_check_register_code_space_validation(buf, size);

	if(offset < aligned_space)
		return 0;

	return 1;
}

/*
 * vikey_logoff, logoff vikey
 * return NO_ERROR if succeed, or error number if not
 */
static int vikey_logoff()
{
	DWORD ret;
	ret = my_VikeyLogoff(vikey_index);
	if(ret != VIKEY_SUCCESS)
		return UKEY_LOGOFF_ERROR;

	return NO_ERROR;
}

/*
 * vikey_load_library, load vikey library
 * return NO_ERROR if succeed, or error number if not
 */
int vikey_load_library()
{
	void* handle;
	char* error;
	int ret;

	ret = vikey_check_library();
	if(!ret)
		return UKEY_LIBRARY_LOAD_ERROR;

	handle = dlopen(VIKEY_SHARED_LIBRARY_PATH, RTLD_NOW);
	if(!handle)
		return UKEY_LIBRARY_LOAD_ERROR;

	my_VikeyFind = dlsym(handle, "VikeyFind");
	if((error = dlerror()) != NULL)
	{
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

	my_VikeyUserLogin = dlsym(handle, "VikeyUserLogin");
	if((error = dlerror()) != NULL)
	{
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

	my_VikeyGetHID = dlsym(handle, "VikeyGetHID");
	if((error = dlerror()) != NULL)
	{
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

	my_VikeyReadData = dlsym(handle, "VikeyReadData");
	if((error = dlerror()) != NULL)
	{
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

	my_ViKeyGetModule = dlsym(handle, "ViKeyGetModule");
	if((error = dlerror()) != NULL)
	{
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

	my_ViKeyCheckModule = dlsym(handle, "ViKeyCheckModule");
	if((error = dlerror()) != NULL)
	{
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

	my_ViKeyDecraseModule = dlsym(handle, "ViKeyDecraseModule");
	if((error = dlerror()) != NULL)
	{
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

	my_VikeyWriteData = dlsym(handle, "VikeyWriteData");
	if((error = dlerror()) != NULL)
	{
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

	my_VikeyLogoff = dlsym(handle, "VikeyLogoff");
	if((error = dlerror()) != NULL)
	{
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

	my_VikeyGetType = dlsym(handle, "VikeyGetType");
	if((error = dlerror()) != NULL)
	{
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

	return NO_ERROR;
}

/*
 * vikey_check_library, check if vikey library is valid
 * return NO_ERROR if succeed, or error number if not
 */
static int vikey_check_library()
{
	return (file_check_md5(VIKEY_SHARED_LIBRARY_PATH, VIKEY_SHARED_LIBRARY_MD5SUM)  &&
	        file_check_sha1(VIKEY_SHARED_LIBRARY_PATH, VIKEY_SHARED_LIBRARY_SHA1SUM) &&
		file_check_sha256(VIKEY_SHARED_LIBRARY_PATH, VIKEY_SHARED_LIBRARY_SHA256SUM));
}

/*
 * vikey_init, init vikey environment
 * return NO_ERROR if succeed, or error number if not
 */
static int vikey_init()
{
	DWORD ret;
	DWORD count;

	VikeyType type;

	ret = vikey_load_library();
	if(ret != NO_ERROR)
		return ret;

	ret = vikey_find(&count);
	if(ret != NO_ERROR)
		return ret;

	ret = vikey_login();
	if(ret != NO_ERROR)
		return ret;

	ret = my_VikeyGetType(vikey_index, &type);
	if(ret != VIKEY_SUCCESS)
		return UKEY_INIT_ERROR;

	if(ViKeyPRO == type)
	{
		USER_SPACE = 2048;
		ADMIN_SPACE_OFFSET = 2048;
	}

	return NO_ERROR;
}

/*
 * vikey_activate_system, activate system with vikey
 * return NO_ERROR if succeed, or error number if not
 */
int vikey_activate_system(const char* license_serial, 
			  const char* kyinfo_ukey, const char* expire_date, 
			  encrypted_number_generator ukey_raw_generator)
{
	DWORD ret = -1;
	char* ukey_serial = NULL;
	char* ukey_expire = NULL;
	char* ukey_expire_no_hyphen = NULL;
	char* raw = NULL;
	char* ukey_hid = NULL;
	int register_code_in_ukey = 0;
	unsigned short is_zero = 1;
	unsigned short can_reduce = 0;
	int offset = -1;
	int aligned_space = -1;
	int result = 0;
	char* register_code = NULL;
	int license_serial_mode = SERIAL_MODE_UNKNOWN;
	int ukey_serial_mode = SERIAL_MODE_UNKNOWN;
	char* ukey_produce = NULL;


	//printf("init\n");
	ret = vikey_init();
	if(ret != NO_ERROR)
		return ret;

	aligned_space = USER_SPACE - (USER_SPACE % SPACE_ALIGNMENT);

	//printf("get data\n");
	ret = vikey_get_data_force();
	if(ret != NO_ERROR)
		goto vikey_activate_system_out;

	//printf("get license key file\n");
	ret = kylin_get_license(&vikey_license_keyfile, (const char*)&vikey_data[ADMIN_SPACE_OFFSET]);
	if(ret != NO_ERROR)
		goto vikey_activate_system_out;

	//printf("get license serial\n");
	ukey_serial = kylin_get_license_serial(vikey_license_keyfile, (const char*)&vikey_data[ADMIN_SPACE_OFFSET]);
	if(!ukey_serial)
	{
		ret = UKEY_NO_SERIAL;
		goto vikey_activate_system_out;
	}

	ukey_produce = kylin_get_license_produce(vikey_license_keyfile, (const char*)&vikey_data[ADMIN_SPACE_OFFSET]);

	ukey_serial_mode = _serial_number_mode(ukey_serial);
	if((SERIAL_MODE_UNKNOWN == ukey_serial_mode) || (SERIAL_MODE_BATCH == ukey_serial_mode))
	{
		ret = UKEY_SERIAL_NUMBER_VALIDATION_ERROR;
		goto vikey_activate_system_out;
	}
	if(SERIAL_MODE_LEGACY == ukey_serial_mode)
	{
		if(strcmp(license_serial, ukey_serial) != 0)
		{
			ret = SERIAL_NUMBER_MATCH_ERROR;
			goto vikey_activate_system_out;
		}
	}

	license_serial_mode = _serial_number_mode(license_serial);
	if((SERIAL_MODE_LEGACY == license_serial_mode) || (SERIAL_MODE_NEW == license_serial_mode))
	{
		if(strcmp(license_serial, ukey_serial) != 0)
		{
			ret = SERIAL_NUMBER_MATCH_ERROR;
			goto vikey_activate_system_out;
		}
	} else if(SERIAL_MODE_BATCH == license_serial_mode) {
		if(ukey_produce)
		{
			if(strcmp(license_serial, ukey_produce) != 0)
			{
				ret = PRODUCE_NUMBER_MATCH_ERROR;
				goto vikey_activate_system_out;
			}
		}

		if(!_product_type_check(license_serial, ukey_serial))
		{
			ret = PRODUCT_TYPE_MATCH_ERROR;
			goto vikey_activate_system_out;
		}
	} else {
		ret = LICENSE_SERIAL_NUMBER_VALIDATION_ERROR;
		goto vikey_activate_system_out;
	}

	//printf("get expire time\n");
	ukey_expire = kylin_get_license_expire(vikey_license_keyfile, (const char*)&vikey_data[ADMIN_SPACE_OFFSET]);
	if(!ukey_expire)
	{
		ret = UKEY_NO_EXPIRE_TIME;
		goto vikey_activate_system_out;
	}

	//printf("expire time remove hyphen\n");
	ukey_expire_no_hyphen = code_remove_hyphen(ukey_expire);
	if(!ukey_expire_no_hyphen)
	{
		ret = UKEY_EXPIRE_DEAL_HYPHEN_ERROR;
		goto vikey_activate_system_out;
	}

	//printf("get ukey hid\n");
	ukey_hid = vikey_get_hid();
	if(!ukey_hid)
	{
		ret = UKEY_GET_HID_ERROR;
		goto vikey_activate_system_out;
	}

	//printf("generate ukey raw\n");
	if(!ukey_raw_generator)
	{
		ret = UKEY_ACTIVATE_SYSTEM_FAILED;
		goto vikey_activate_system_out;
	}

	register_code = _kylin_activation_get_register_number(ukey_serial, &result);
	if(result != NO_ERROR)
	{
		ret = result;
		goto vikey_activate_system_out;
	}
	if(!register_code)
	{
		ret = REGISTER_CREATION_ERROR;
		goto vikey_activate_system_out;
	}

	raw = ukey_raw_generator(register_code, ukey_hid, ukey_expire_no_hyphen);
	if(!raw)
	{
		ret = UKEY_ACTIVATE_SYSTEM_FAILED;
		goto vikey_activate_system_out;
	}


	if(!kyinfo_ukey && !expire_date)
		goto ukey_do_activation;

	if(!kyinfo_ukey && expire_date)
	{
		if(_same_expire_date(ukey_expire, expire_date))
		{
			ret = HAS_NORMALLY_ACTIVATED;
			goto vikey_activate_system_out;
		}
	}

	if(kyinfo_ukey && expire_date)
	{
		if(_same_ukey_hid(ukey_hid, kyinfo_ukey))
		{
			goto ukey_do_activation;
		}
		else
		{
			if(_same_expire_date(ukey_expire, expire_date))
			{
				ret = HAS_ACTIVATED_WITH_ANOTHER_UKEY;
				goto vikey_activate_system_out;
			}
			else
				goto ukey_do_activation;
		}
	}

ukey_do_activation:
	//printf("find register code\n");
	register_code_in_ukey = kylin_find_register_code(register_code, (const char*)vikey_data, USER_SPACE);
	if(register_code_in_ukey)
	{
		ret = _kylin_activation_activate_system(raw, ukey_hid, ukey_serial, 0);
		goto vikey_activate_system_out;
	}

	//printf("check module\n");
	ret = vikey_check_module(&is_zero, &can_reduce);
	if(ret != NO_ERROR)
		goto vikey_activate_system_out;

	//printf("is_zero\n");
	if(is_zero)
	{
		ret = UKEY_MODULE_IS_ZERO;
		goto vikey_activate_system_out;
	}

	//printf("can_reduce\n");
	if(!can_reduce)
	{
		ret = UKEY_MODULE_CANNOT_REDUCE;
		goto vikey_activate_system_out;
	}

	if(vikey_space_is_full((const char*)vikey_data, USER_SPACE))
	{
		ret = UKEY_SPACE_FULL;
		goto vikey_activate_system_out;
	}

	//printf("activate system test\n");
	ret = _kylin_activation_activate_system_test(raw, ukey_hid, ukey_serial);
	if(ret != NO_ERROR)
		goto vikey_activate_system_out;

    /*
     * do not decrease module here
     */
    /*
	//printf("decrease module\n");
	ret = vikey_decrease_module();
	if(ret != NO_ERROR)
		goto vikey_activate_system_out;
    */

	//printf("activate system\n");
	ret = _kylin_activation_activate_system(raw, ukey_hid, ukey_serial, 0);
	if(ret != NO_ERROR)
		goto vikey_activate_system_out;

	//printf("add register code\n");
	ret = vikey_add_register_code(register_code, &offset);
	if(ret != NO_ERROR)
		goto vikey_activate_system_out;

    // decrease module here
	//printf("decrease module\n");
	ret = vikey_decrease_module();
	if(ret != NO_ERROR)
	{
		goto vikey_activate_system_out;
	}

	//printf("sync data\n");
	if((-1 != offset) && (0 == (offset % ENCRYPTED_NUMBER_LENGTH)) && (offset < aligned_space))
		memcpy(&vikey_data[offset], register_code, ENCRYPTED_NUMBER_LENGTH);
	else
		vikey_get_data_force();

	ret = NO_ERROR;
vikey_activate_system_out:

	if(register_code)
		free(register_code);
	if(ukey_serial)
		free(ukey_serial);
	if(ukey_expire)
		free(ukey_expire);
	if(ukey_expire_no_hyphen)
		free(ukey_expire_no_hyphen);
	if(ukey_produce)
		free(ukey_produce);
	if(raw)
		free(raw);
	if(ukey_hid)
		free(ukey_hid);

	//printf("logoff\n");
	vikey_logoff();
	return ret;
}

/*
 * vikey_dump_basic_info, dump vikey basic information to file f
 * return NO_ERROR if succeed, or error number if not
 */
static int vikey_dump_basic_info(FILE *f, int display)
{
	int ret = -1;
	char* ukey_hid = NULL;
	char* ukey_serial = NULL;
	char* ukey_expire = NULL;
	char* ukey_to = NULL;
	unsigned short module_count = 0;
	char* ukey_produce = NULL;

	char line[MAX_LENGTH];
	char message[MAX_LENGTH];

	ukey_hid = vikey_get_hid();
	if(!ukey_hid)
	{
		ret = UKEY_GET_HID_ERROR;
		goto vikey_dump_base_out;
	}

	ukey_serial = kylin_get_license_serial(vikey_license_keyfile, (const char*)&vikey_data[ADMIN_SPACE_OFFSET]);
	if(!ukey_serial)
	{
		ret = UKEY_NO_SERIAL;
		goto vikey_dump_base_out;
	}

	ukey_expire = kylin_get_license_expire(vikey_license_keyfile, (const char*)&vikey_data[ADMIN_SPACE_OFFSET]);
	if(!ukey_expire)
	{
		ret = UKEY_NO_EXPIRE_TIME;
		goto vikey_dump_base_out;
	}

	ret = vikey_get_module(&module_count);
	if(ret != NO_ERROR)
		goto vikey_dump_base_out;

	ukey_to = kylin_get_license_to(vikey_license_keyfile, (const char*)&vikey_data[ADMIN_SPACE_OFFSET]);

	ukey_produce = kylin_get_license_produce(vikey_license_keyfile, (const char*)&vikey_data[ADMIN_SPACE_OFFSET]);

	memset(line, 0, MAX_LENGTH);
	memset(message, 0, MAX_LENGTH);

	sprintf(line, "ukeyid=%s\nserial=%s\nterm=%s\nmodule_count=%d\n", 
		ukey_hid, ukey_serial, ukey_expire, module_count);

	strcat(message, line);

	if(ukey_to)
	{
		memset(line, 0, MAX_LENGTH);
		sprintf(line, "to=%s\n", ukey_to);
		strcat(message, line);
	}

	if(ukey_produce)
	{
		memset(line, 0, MAX_LENGTH);
		sprintf(line, "produce=%s\n", ukey_produce);
		strcat(message, line);
	}

	strcat(message, "\n");

	if(display)
		fprintf(f, "%s", message);

	strcat(dump_message, message);

	ret = NO_ERROR;

vikey_dump_base_out:
	if(ukey_hid)
		free(ukey_hid);
	if(ukey_serial)
		free(ukey_serial);
	if(ukey_expire)
		free(ukey_expire);
	if(ukey_to)
		free(ukey_to);
	if(ukey_produce)
		free(ukey_produce);

	return ret;
}

/*
 * vikey_dump_register code, dump vikey register code to file f
 * return NO_ERROR if succeed, or error number if not
 */
static void vikey_dump_register_code(FILE *f, int display)
{
	int _index = 0;
	int count = 0;
	char buf[ ENCRYPTED_NUMBER_LENGTH + 1];
	char message[MAX_LENGTH];
	char line[LINE_SIZE];

	memset(buf, 0, ENCRYPTED_NUMBER_LENGTH + 1);
	memset(message, 0, MAX_LENGTH);
	memset(line, 0, LINE_SIZE);

	count = USER_SPACE - (USER_SPACE % SPACE_ALIGNMENT);

	for(_index = 0; _index < count; _index += ENCRYPTED_NUMBER_LENGTH)
	{
		memcpy(buf, &vikey_data[_index], ENCRYPTED_NUMBER_LENGTH);
		if(register_code_is_valid(buf))
		{
			sprintf(line, "%s\n", buf);
			strcat(message, line);
		}
	}

	if(display)
		fprintf(f, "%s", message);

	strcat(dump_message, message);
}

/*
 * _vikey_dump, dump vikey information to file f
 * encrypt: whether encryption is needed or not
 * display_all: whether display all information or not
 * return NO_ERROR if succeed, or error number if not
 */
static int _vikey_dump(FILE* f, int encrypt, int display_all)
{
	DWORD ret = -1;

	int display_basic = 1;
	int display_register_code = 0;

	char* encrypted_message = NULL;
	unsigned long encrypted_message_size = -1;

	ret = vikey_init();
	if(ret != NO_ERROR)
		return ret;

	ret = vikey_get_data_force();
	if(ret != NO_ERROR)
		return ret;

	memset(dump_message, 0, sizeof(dump_message));

	if(display_all)
	{
		display_basic = 1;
		display_register_code = 1;
	}

	if(encrypt)
	{
		display_basic = 0;
		display_register_code = 0;
	}

	ret = vikey_dump_basic_info(f, display_basic);
	if(ret != NO_ERROR)
		return ret;

	vikey_dump_register_code(f, display_register_code);

	if(encrypt)
	{
		ret = gpg_encrypt(dump_message, &encrypted_message, &encrypted_message_size);
		if(ret != NO_ERROR)
			return ret;

		if(encrypted_message && (encrypted_message_size > 0))
		{
			fprintf(f, "%s", encrypted_message);
		}
	}

	return NO_ERROR;
}

/*
 * vikey_dump, dump vikey information to file f
 * encrypt: whether encryption is needed or not
 * display_all: whether display all information or not
 * return NO_ERROR if succeed, or error number if not
 */
int vikey_dump(FILE* f, int encrypt, int display_all)
{
	return _vikey_dump(f, encrypt, display_all);
}

#if 0

int main(int argc, char** argv)
{
	int ret;
	char* hid = NULL;
	char* license_serial = NULL;
	char* license_expire = NULL;
	int count;
	unsigned short module_count;
	unsigned short is_zero;
	unsigned short can_reduce;
	

	ret = vikey_load_library();
	if(ret != NO_ERROR)
		printf("file:%s line:%d   %d\n", __FILE__, __LINE__, ret);

	ret = vikey_init();
	if(ret != NO_ERROR)
		printf("file:%s line:%d   %d\n", __FILE__, __LINE__, ret);

	ret = vikey_init();
	if(ret != NO_ERROR)
		printf("file:%s line:%d   %d\n", __FILE__, __LINE__, ret);

	hid = vikey_get_hid();
	if(hid)
		printf("file:%s line:%d   hid %s\n", __FILE__, __LINE__, hid);
	else
		printf("file:%s line:%d   no hid\n", __FILE__, __LINE__);

	ret = vikey_get_data(0);
	if(ret != NO_ERROR)
		printf("file:%s line:%d   %d\n", __FILE__, __LINE__, ret);

	ret = kylin_get_license();
	if(ret != NO_ERROR)
		printf("file:%s line:%d   %d\n", __FILE__, __LINE__, ret);

	count = kylin_get_license_count();
		printf("file:%s line:%d   count %d\n", __FILE__, __LINE__, count);

	license_serial = kylin_get_license_serial(vikey_license_keyfile, vikey_data + ADMIN_SPACE_OFFSET);
	if(license_serial)
		printf("file:%s line:%d   serial %s\n", __FILE__, __LINE__, license_serial);
	else
		printf("file:%s line:%d   no serial\n", __FILE__, __LINE__);


	license_expire = kylin_get_license_expire(vikey_license_keyfile, vikey_data + ADMIN_SPACE_OFFSET);
	if(license_expire)
		printf("file:%s line:%d   expire %s\n", __FILE__, __LINE__, license_expire);
	else
		printf("file:%s line:%d   no expire\n", __FILE__, __LINE__);

	ret = vikey_get_module(&module_count);
	if(ret != NO_ERROR)
		printf("file:%s line:%d   get module count failed\n", __FILE__, __LINE__);
	else
		printf("file:%s line:%d   module count %d\n", __FILE__, __LINE__, module_count);
	
	ret = vikey_check_module(&is_zero, &can_reduce);
	if(ret != NO_ERROR)
		printf("file:%s line:%d   get check module failed\n", __FILE__, __LINE__);
	else
		printf("file:%s line:%d   is_zero %d   can_reduce %d\n", __FILE__, __LINE__, is_zero, can_reduce);
	
	ret = kylin_find_register_code((const char*)"SDFKPPEEJ3RCRDXSCU3K", vikey_data, sizeof(vikey_data));
		printf("file:%s line:%d   find register code \"%s\": %s\n", __FILE__, __LINE__, "SDFKPPEEJ3RCRDXSCU3K", ret?"yes":"no");

	ret = vikey_add_register_code("SDFKPPEEJ3RCRDXSCU3K");
	if(ret != NO_ERROR)
		printf("file:%s line:%d   add register code failed %d\n", __FILE__, __LINE__, ret);
	else
		printf("file:%s line:%d   add register code succeeded\n", __FILE__, __LINE__);

	ret = kylin_find_register_code((const char*)"SDFKPPEEJ3RCRDXSCU3K", vikey_data, sizeof(vikey_data));
		printf("file:%s line:%d   find register code \"%s\": %s\n", __FILE__, __LINE__, "SDFKPPEEJ3RCRDXSCU3K", ret?"yes":"no");

	ret = kylin_find_register_code((const char*)"HW5PPRPBQZHKF4L22GHW", vikey_data, sizeof(vikey_data));
		printf("file:%s line:%d   find register code \"%s\": %s\n", __FILE__, __LINE__, "HW5PPRPBQZHKF4L22GHW", ret?"yes":"no");

	ret = vikey_logoff();
	if(ret != NO_ERROR)
		printf("file:%s line:%d   %d\n", __FILE__, __LINE__, ret);

	return 0;
}

#endif

#endif // ENABLE_VIKEY
